﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using PowerMonitor.Properties;

namespace PowerMonitor
{
    public partial class ComAssistant : baseDockForm
    {
        public ComAssistant()
        {
            InitializeComponent();
        }
        private void ComAssistant_Load(object sender, EventArgs e)
        {
            RefreshPortPara();
            cbbCheck.SelectedIndex = 3;
            toolTip1.SetToolTip(txtSend, "按下Enter键发送" + "\n" + "按下Ctrl+Enter输入多条报文");
            if (File.Exists(AppMain.StartPath + "\\DefaultMsg.txt"))
            {
                txtSend.LoadFile(AppMain.StartPath + "\\DefaultMsg.txt", RichTextBoxStreamType.PlainText);
            }
        }
        int RxTxTimeOut = 250, RxInTimeOut = 20; // 分别为帧之间接收超时  帧内部（两字节之间）接收超时
        
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            if (_sendByteArray == null || _sendByteArray.Count == 0)
            {
                return;
            }
            
            //ComDevice.ReadTimeout = RxTxTimeOut;               // 超时时间（ms）
            ComDevice.ReadTimeout = 25;  
            int startIndex = 0;
            StringBuilder strB = new StringBuilder(); // 处理显示字符
            List<byte> RecBytelist = new List<byte>();
            Stopwatch Watch = new Stopwatch(); // 发送结束至接收第一个字节之间的时间
            int SendIndex = 0;  // 当前发送数据
            bool isLast = false; // 多条发送时的最后一条
            byte[] bySend = null;
            while (!backgroundWorker1.CancellationPending)
            {
                try
                {
                    ComDevice.DiscardOutBuffer();
                    ComDevice.DiscardInBuffer();
                    bySend = _sendByteArray[SendIndex];
                    ComDevice.Write(bySend, 0, bySend.Length);
                    SendIndex++;
                    if (SendIndex>=_sendByteArray.Count)
                    {
                        SendIndex = 0;
                        isLast = true;
                    }
                    Watch.Reset();
                    Watch.Start();
                    /////// 显示发送内容/////////////
                    strB.Clear();
                    for (int i = 0; i < bySend.Length; i++)
                    {
                        strB.Append(bySend[i].ToString("X2") + " ");
                    }
                    AppMain.MainForm.Invoke(new MethodInvoker(delegate()
                    {
                        txtSndRec.AppendText(strB.ToString() + "\n");
                    }));
                    if (!ckbAutoSend.Checked)
                    {
                        ComDevice.ReadTimeout = 250;  
                    }
                    else
                    {
                        Thread.Sleep(RxTxTimeOut);
                    }
                    // 显示接收内容
                    int iRec = -1;
                    iRec = ComDevice.ReadByte(); // 此处超时认为发送报文后 没有接收
                    if (iRec != -1)
                    {
                        Watch.Stop();
                        AppMain.MainForm.Invoke(new MethodInvoker(delegate()
                        {
                            startIndex = txtSndRec.Text.Length;
                            txtSndRec.AppendText("(" + Watch.ElapsedMilliseconds.ToString() + " ms)\n"); // 接收时间
                            txtSndRec.Select(startIndex, txtSndRec.Text.Length - startIndex);
                            txtSndRec.SelectionColor = Color.Gray;
                            //txtSndRec.ScrollToCaret();
                        }));
                    }
                    if (!ckbAutoSend.Checked)
                    {
                        ComDevice.ReadTimeout = 25;
                    }
                    while (iRec != -1) // 测试发现没有出现-1  而是最后读取超时
                    {
                        RecBytelist.Add((byte)iRec);
                        iRec = ComDevice.ReadByte(); // 超时会引发异常（接收第一个字节后超时,作为结束判断）
                    }

                    AppMain.MainForm.Invoke(new MethodInvoker(delegate()
                    {
                        txtSndRec.ScrollToCaret();
                    }));

                }
                catch (Exception ex)
                {
                    // ComDevice.ReadTimeout = RxTxTimeOut;
                    if (RecBytelist.Count > 0)
                    {
                        AppMain.MainForm.Invoke(new MethodInvoker(delegate()
                        {
                            strB.Clear();
                            for (int i = 0; i < RecBytelist.Count; i++)
                            {
                                strB.Append(RecBytelist[i].ToString("X2") + " ");
                            }
                            startIndex = txtSndRec.Text.Length;
                            txtSndRec.AppendText(strB.ToString());
                            txtSndRec.Select(startIndex, strB.Length);
                            txtSndRec.SelectionColor = Color.Blue;
                            txtSndRec.AppendText("\n");
                            txtSndRec.ScrollToCaret();
                        }));
                    }
                    else // 没有接收
                    {
                        AppMain.MainForm.Invoke(new MethodInvoker(delegate()
                        {
                            int Index = txtSndRec.Text.Length;
                            txtSndRec.AppendText("(" + Watch.ElapsedMilliseconds.ToString() + " ms) " + ex.Message + "\n");
                            txtSndRec.Select(Index, ex.Message.Length + 20);
                            txtSndRec.SelectionColor = Color.Coral;
                            txtSndRec.ScrollToCaret();
                        }));
                    }
                    
                    RecBytelist.Clear();
                    //Console.WriteLine(ex.Message);
                    string str = ex.GetType().Name;
                    if (str != "TimeoutException" && isLast) // isLast作为判断最后一条发送报文标志
                    {
                        return;
                    }
                }
                finally
                {
                    if (ckbAutoSend.CheckState == CheckState.Unchecked && isLast)
                    {
                        backgroundWorker1.CancelAsync();
                    }
                }
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (ComDevice.IsOpen && ckbAutoSend.CheckState == CheckState.Checked)
            {
                 ComDevice.Close();
            }
        }

        private bool _isSendTextChange = true; // 输入字符串改变
        private void txtSend_TextChanged(object sender, EventArgs e)
        {
            _isSendTextChange = true;
        }
        private void ComDevice_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {
                return;
            }
            StringBuilder strB = new StringBuilder();
            int startIndex;
            byte[] recBytelist = new byte[ComDevice.BytesToRead];
            ComDevice.Read(recBytelist, 0, ComDevice.BytesToRead);
            for (int i = 0; i < recBytelist.Length; i++)
            {
                strB.Append(recBytelist[i].ToString("X2") + " ");
            }
            Application.DoEvents();
            AppMain.MainForm.Invoke(new MethodInvoker(delegate()
            {
                startIndex = txtSndRec.Text.Length;
                txtSndRec.AppendText(strB.ToString());
                txtSndRec.Select(startIndex, strB.Length);
                txtSndRec.SelectionColor = Color.Blue;
                txtSndRec.ScrollToCaret();
                txtSndRec.SelectionStart = txtSndRec.Text.Length;
            }));
        }

        private readonly List<byte[]> _sendByteArray = new List<byte[]>();
        //private ArrayList SendByteArray = new ArrayList(); // 发送数据
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {
                if (btnSend.Text == "停止(Stop)")
                {
                    backgroundWorker1.CancelAsync();
                }
                return;
            }
            if (_isSendTextChange)
            {
                _isSendTextChange = false;
                _sendByteArray.Clear();
                StringBuilder strBd = new StringBuilder();
                string[] strInput = txtSend.Text.Split('\n');

                for (int i = 0; i < strInput.Length; i++)
                {
                    strBd.Clear();
                    strBd.Append(strInput[i].Trim());
                    strBd.Replace(" ", null);
                    if (strBd.Length == 0)
                    {
                        continue;
                    }
                    if (strBd.Length % 2 != 0 || strBd.Length < 2)
                    {
                        MessageBox.Show("输入报文有误!");
                        continue;
                    }

                    byte[] bData = new byte[strBd.Length / 2];
                    byte[] bSend = null;  // 发送的报文
                    for (int j = 0; j < strBd.Length / 2; j++)
                    {
                        bData[j] = Convert.ToByte(strBd.ToString(2 * j, 2), 16);
                    }
                    try
                    {
                        switch (cbbCheck.SelectedIndex)
                        {
                            case 1:
                                bSend = new byte[bData.Length + 1];
                                Buffer.BlockCopy(bData, 0, bSend, 0, bData.Length);
                                bSend[bData.Length] = Check.CheckXOR(bData, bData.Length);
                                break;
                            case 2:
                                bSend = new byte[bData.Length + 1];
                                Buffer.BlockCopy(bData, 0, bSend, 0, bData.Length);
                                bSend[bData.Length] = Check.CheckSum(bData, bData.Length);
                                break;
                            case 3:
                                ushort CRC = 0;
                                bSend = new byte[bData.Length + 2];
                                Buffer.BlockCopy(bData, 0, bSend, 0, bData.Length);
                                CRC = Check.CheckCRC16(bData, bData.Length);
                                bSend[bData.Length] = (byte)(CRC & 0x00FF);
                                bSend[bData.Length + 1] = (byte)(CRC >> 8);
                                break;
                            case 4:
                                bSend = new byte[bData.Length + 1];
                                Buffer.BlockCopy(bData, 0, bSend, 0, bData.Length);
                                bSend[bData.Length] = Check.CheckLRC(bData, bData.Length);
                                break;
                            default:
                                bSend = new byte[bData.Length];
                                Buffer.BlockCopy(bData, 0, bSend, 0, bData.Length);
                                break;
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, Properties.Resources.msgTitleTip, MessageBoxButtons.OK, MessageBoxIcon.Information);
                        return;
                    }
                    _sendByteArray.Add(bSend);
                }
                if (_sendByteArray.Count == 0)
                {
                    return;
                }
            } 
            else
            {
            }

            
            RxTxTimeOut = (int)numTimeDly.Value;
            RxInTimeOut = (int)numTimeout.Value;
            if (!ComDevice.IsOpen)
            {
                try
                {
                    ComDevice.PortName = cbComPortId.Text.Trim();
                    ComDevice.BaudRate = Convert.ToInt32(cbBaudrate.SelectedItem.ToString());
                    ComDevice.Parity = (Parity)Convert.ToInt32(cbParity.SelectedIndex.ToString());
                    ComDevice.DataBits = Convert.ToInt32(cbDatabit.SelectedItem.ToString());
                    if (cbStopbit.SelectedIndex == 1)
                    {
                        ComDevice.StopBits = StopBits.OnePointFive;
                    }
                    else
                    {
                        ComDevice.StopBits = (StopBits)Convert.ToInt32(cbStopbit.SelectedItem.ToString());
                    }
                    ComDevice.Open();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, Properties.Resources.msgTitleTip, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }
            }
            backgroundWorker1.RunWorkerAsync();
        }
        /// <summary>
        /// 回车发送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSend_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && !e.Control)
            {
                if (backgroundWorker1.IsBusy)
                {
                    e.Handled = true;
                    return;
                }
                btnSend_Click(sender, EventArgs.Empty);
                e.Handled = true;
            }
        }
        /// <summary>
        /// 回车发送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 0x0d)
            {
                e.Handled = true;
            }
        }
        /// <summary>
        /// 清空报文
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsmiClearRxTx_Click(object sender, EventArgs e)
        {
            txtSndRec.Clear();
        }
        /// <summary>
        /// 界面状态显示处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            numTimeDly.Enabled = !backgroundWorker1.IsBusy;
            numTimeout.Enabled = !backgroundWorker1.IsBusy;
            cbbCheck.Enabled = !backgroundWorker1.IsBusy;
            cbComPortId.Enabled = !ComDevice.IsOpen;
            cbBaudrate.Enabled = !ComDevice.IsOpen;
            cbParity.Enabled = !ComDevice.IsOpen;
            cbDatabit.Enabled = !ComDevice.IsOpen;
            cbStopbit.Enabled = !ComDevice.IsOpen;
            btnRefreshPort.Enabled = !ComDevice.IsOpen;

            if (backgroundWorker1.IsBusy && _sendByteArray.Count > 2 || (ckbAutoSend.Checked && backgroundWorker1.IsBusy))
            {
                btnSend.Text = "停止(Stop)";
                btnSend.BackColor = Color.Coral;
            }
            else
            {
                btnSend.Text = "发送(Send)";
                btnSend.BackColor = Color.LightGreen;
            }
            if (ComDevice.IsOpen)
            {
                btnOpen.Text = "关闭";
                picComStatus.Image = Resources.StartSta;
            }
            else
            {
                btnOpen.Text = "打开";
                picComStatus.Image = Resources.StopSta;
            }
            if (txtSndRec.TextLength > 100000 && backgroundWorker1.IsBusy) // 清空报文
            {
                txtSndRec.Clear();
            }
        }

        private void btnRefreshPort_Click(object sender, EventArgs e)
        {
            if (!ComDevice.IsOpen)
            {
                cbComPortId.Items.Clear();
                cbComPortId.Items.AddRange(SerialPort.GetPortNames());
                if (cbComPortId.Items.Count > 0)
                {
                    if (SerialPort.GetPortNames().Contains(ComDevice.PortName))
                    {
                        cbComPortId.Text = ComDevice.PortName;
                    }
                    else
                    {
                        cbComPortId.SelectedIndex = cbComPortId.Items.Count - 1;
                    }
                }
            }
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            if (ComDevice.IsOpen == false)
            {
                try
                {
                    if (cbComPortId.SelectedItem == null)
                    {
                        MessageBox.Show("请选择串口", Properties.Resources.msgTitleTip, MessageBoxButtons.OK, MessageBoxIcon.Information);
                        return;
                    }
                    ComDevice.PortName = cbComPortId.SelectedItem.ToString();
                    ComDevice.BaudRate = Convert.ToInt32(cbBaudrate.SelectedItem.ToString());
                    ComDevice.Parity = (Parity)Convert.ToInt32(cbParity.SelectedIndex.ToString());
                    ComDevice.DataBits = Convert.ToInt32(cbDatabit.SelectedItem.ToString());
                    if (cbStopbit.SelectedIndex == 1)
                    {
                        ComDevice.StopBits = StopBits.OnePointFive;
                    }
                    else
                    {
                        ComDevice.StopBits = (StopBits)Convert.ToInt32(cbStopbit.SelectedItem.ToString());
                    }
                    ComDevice.Open();
                }
                catch (Exception ep)
                {
                    MessageBox.Show(ep.Message);
                    return;
                }
            }
            else
            {
                ComDevice.Close();
            }
        }

        /// <summary>
        /// 刷新可用端口
        /// </summary>
        private void RefreshPortPara()
        {
            string[] StrStopbit = { "One", "OnePointFive", "Two" };
            int Index = 0;
            try
            {
                
                cbComPortId.Text = ComDevice.PortName;
                cbBaudrate.Text = ComDevice.BaudRate.ToString();
                cbParity.Text = ComDevice.Parity.ToString();
                cbDatabit.Text = ComDevice.DataBits.ToString();
                for (int i = 0; i < StrStopbit.Length; i++)
                {
                    if (StrStopbit[i] == ComDevice.StopBits.ToString())
                    {
                        break;
                    }
                    Index++;
                }
                if (Index < cbStopbit.Items.Count)
                {
                    cbStopbit.SelectedIndex = Index;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

            if (ComDevice.IsOpen == false)
            {
                try
                {
                    cbComPortId.Items.Clear();
                    cbComPortId.Items.AddRange(SerialPort.GetPortNames());
                    if (cbComPortId.Items.Count > 0)
                    {
                        if (SerialPort.GetPortNames().Contains(ComDevice.PortName))
                        {
                            cbComPortId.Text = ComDevice.PortName;
                        }
                        else
                        {
                            cbComPortId.SelectedIndex = cbComPortId.Items.Count - 1;
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

        private void textBytes_KeyUp(object sender, KeyEventArgs e)
        {
            if (textBytes.Text.Trim().Length < 1)
            {
                return;
            }
            StringBuilder strBd = new StringBuilder();
            strBd.Append(textBytes.Text.Trim());
            strBd.Replace(" ", null);
            byte[] bData = { 0, 0, 0, 0 };
            try
            {
                for (int i = 0; i < strBd.Length / 2 && i < 4; i++)
                {
                    bData[i] = Convert.ToByte(strBd.ToString(2 * i, 2), 16);
                }

                if (lbType.Text == "浮点")
                {
                    Array.Reverse(bData);
                    float fres = BitConverter.ToSingle(bData, 0);
                    txtFloat.Text = fres.ToString();
                }
                else
                {
                    Array.Reverse(bData, 0, strBd.Length / 2);
                    int iRes = BitConverter.ToInt32(bData, 0);
                    txtFloat.Text = iRes.ToString();
                }
            }
            catch
            { }
        }

        private void txtFloat_KeyUp(object sender, KeyEventArgs e)
        {
            byte[] bData = new byte[4];
            float fData = 0;
            Int32 iData = 0;
            try
            {
                if (e.KeyCode != Keys.None)
                {
                    if (lbType.Text == "浮点")
                    {
                        fData = float.Parse(txtFloat.Text);
                        bData = BitConverter.GetBytes(fData);
                    }
                    else
                    {
                        iData = Int32.Parse(txtFloat.Text);
                        bData = BitConverter.GetBytes(iData);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }

            Array.Reverse(bData);
            StringBuilder strBd = new StringBuilder();
            for (int i = 0; i < 4; i++)
            {
                if (i < 3)
                {
                    strBd.Append(bData[i].ToString("X2") + " ");
                }
                else
                {
                    strBd.Append(bData[i].ToString("X2"));
                }
            }
            textBytes.Text = strBd.ToString();
        }

        private void lbType_Click(object sender, EventArgs e)
        {
            if (lbType.Text == "浮点")
            {
                lbType.Text = "整型";
                lbType.BackColor = Color.YellowGreen;
            }
            else
            {
                lbType.Text = "浮点";
                lbType.BackColor = Color.LightSteelBlue;
            }
        }

        private void btnExport_Click(object sender, EventArgs e)
        {
            if (txtSend.TextLength <= 0)
            {
                MessageBox.Show("没有可保存的内容!");
                return;
            }
            SaveFileDialog save = new SaveFileDialog();
            save.DefaultExt = "ini";
            save.AddExtension = true;
            save.Filter = "(.txt)|*.txt";
            save.Title = "请选择要导出数据的位置";
            //save.InitialDirectory = AppMain.StartPath;
            if (save.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            txtSend.SaveFile(save.FileName, RichTextBoxStreamType.PlainText);

        }

        private void btnImport_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofDlg = new OpenFileDialog();
            //ofDlg.InitialDirectory = AppMain.StartPath;
            ofDlg.Filter = "(.txt)|*.txt";
            ofDlg.Multiselect = false;
            if (ofDlg.ShowDialog() == DialogResult.OK)
            {
                txtSend.LoadFile(ofDlg.FileName, RichTextBoxStreamType.PlainText);
            }
        }

        private void ComAssistant_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (txtSend.TextLength > 0)
            {
                txtSend.SaveFile(AppMain.StartPath + "\\DefaultMsg.txt", RichTextBoxStreamType.PlainText);
            } 
        }


    }

    public sealed class Check
    {
        /// <summary>
        ///    1.校验和（返回一个字节）
        ///    函数功能 : 校验和，前n个字节数据之和模256 
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static byte CheckSum(byte[] buf, int len)
        {
            int iSum = 0, bylen = 0;
            bylen = len;
            if (buf.Length < len)
            {
                bylen = buf.Length;
            }
            for (int i = 0; i < bylen; i++)
            {
                iSum += buf[i];
            }
            iSum %= 0x100;
            return (byte)iSum;
        }

        /// <summary>
        /// 2.异或校验（返回一个字节）
        /// </summary>
        /// <param name="strData"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static byte CheckXOR(byte[] strData, int len)
        {
            byte checksum = 0;
            int bylen = 0;
            bylen = len;
            if (strData.Length < len)
            {
                bylen = strData.Length;
            }
            for (int i = 0; i < bylen; i++)
            {
                checksum = (byte)(checksum ^ strData[i]);
            }
            return checksum;
        }


        /// <summary>
        /// 3.CRC校验（返回2个字节，高字节在前，低字节在后）
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="leng"></param>
        /// <returns></returns>
        public static ushort CheckCRC16(byte[] buf, int leng)
        {
            ushort wCRC = 0xFFFF;
            int bylen = 0;
            bylen = leng;
            if (buf.Length < leng)
            {
                bylen = buf.Length;
            }
            for (int i = 0; i < bylen; i++)
            {
                wCRC = (ushort)(wCRC ^ buf[i]);
                for (int j = 0; j < 8; j++)
                {
                    if ((wCRC & 0x01) == 1)
                    {
                        wCRC = (ushort)((wCRC >> 1) ^ 0xA001);
                    }
                    else
                    {
                        wCRC = (ushort)((wCRC) >> 1);
                    }
                }
            }
            return wCRC;
        }


        /// <summary>
        /// 4.LRC纵向冗余校验（返回一个字节）
        /// ASCII 方式时使用
        /// </summary>
        /// <param name="auchMsg"></param>
        /// <param name="Len"></param>
        /// <returns></returns>
        public static byte CheckLRC(byte[] auchMsg, int Len)
        {
            int bylen = 0, i = 0;
            byte uchLRC = 0;
            bylen = Len;
            if (auchMsg.Length < Len)
            {
                bylen = auchMsg.Length;
            }

            while (bylen-- > 0)
            {
                uchLRC += auchMsg[i];
                i++;
            }
            return ((byte)(-(sbyte)uchLRC));
        }

        /// <summary>
        /// 补码求和（返回一个字节）
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static byte CheckNSum(byte[] buf, int len)
        {
            int iSum = 0;
            int bylen = 0;
            bylen = len;
            if (buf.Length < len)
            {
                bylen = buf.Length;
            }
            for (int i = 0; i < bylen; i++)
            {
                iSum += buf[i];
            }
            iSum = 256 - iSum;
            return (byte)iSum;
        }

    }
}
